package com.ai.bd_util;

/**
 * ClassName:    BaiduAuthUtil
 * Description:  TODO
 * Author:       yuzx
 * Date:         2025/9/9 09:30
 * Version:      1.0
 **/
import okhttp3.*;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 百度AI鉴权工具类（严格遵循官方OkHttp示例规范）
 * 解决问题：1. 修正Content-Type为application/json；2. 参数通过URL查询字符串传递；3. 使用官方推荐的OkHttp
 */
@Component
public class BaiduAuthUtil {
    // 日志打印（便于排查问题）
    private static final Logger log = LoggerFactory.getLogger(BaiduAuthUtil.class);

    // 百度鉴权配置（从application.yml读取，需确保配置正确）
    @Value("${baidu.ai.auth.api-key}")
    private String apiKey; // 对应官方的client_id
    @Value("${baidu.ai.auth.secret-key}")
    private String secretKey; // 对应官方的client_secret
    @Value("${baidu.ai.auth.token-url}")
    private String tokenUrl; // 官方鉴权URL：https://aip.baidubce.com/oauth/2.0/token

    // OkHttp客户端（官方示例使用，全局单例，避免重复创建）
    private OkHttpClient httpClient;

    // 原子类存储token和过期时间（保证线程安全，避免并发问题）
    private final AtomicReference<String> accessToken = new AtomicReference<>();
    private final AtomicLong expireTime = new AtomicLong(0); // 过期时间（毫秒时间戳）

    /**
     * 初始化OkHttp客户端（PostConstruct：Bean创建后自动执行）
     */
    @PostConstruct
    public void initOkHttpClient() {
        this.httpClient = new OkHttpClient().newBuilder().build();
        log.info("OkHttp客户端初始化完成");
    }

    /**
     * 获取有效access_token（核心方法，过期自动刷新）
     * @return 有效access_token
     * @throws IOException    HTTP请求异常
     * @throws JSONException  JSON解析异常
     * @throws Exception      鉴权失败异常（如AK/SK错误）
     */
    public String getValidAccessToken() throws IOException, JSONException, Exception {
        // 1. 检查token是否存在且未过期（提前10分钟刷新，避免网络延迟导致过期）
        long currentTime = System.currentTimeMillis();
        if (accessToken.get() != null && currentTime < expireTime.get()) {
            log.info("使用缓存的access_token，有效期至：{}", new Date(expireTime.get()));
            return accessToken.get();
        }

        // 2. 双重检查锁：避免并发场景下重复请求鉴权接口
        synchronized (this) {
            if (accessToken.get() != null && currentTime < expireTime.get()) {
                log.info("并发场景下，使用已缓存的access_token");
                return accessToken.get();
            }

            // 3. 构造官方规范的鉴权URL（参数拼在URL后，对应官方示例的URL格式）
            String authUrl = String.format(
                    "%s?client_id=%s&client_secret=%s&grant_type=client_credentials",
                    tokenUrl, apiKey, secretKey
            );
            log.info("百度鉴权请求URL：{}", authUrl);

            // 4. 构造请求头（严格匹配官方：Content-Type=application/json，Accept=application/json）
            Headers headers = new Headers.Builder()
                    .add("Content-Type", "application/json")
                    .add("Accept", "application/json")
                    .build();

            // 5. 构造请求体（官方示例：空的JSON）
            MediaType mediaType = MediaType.parse("application/json");
            RequestBody requestBody = RequestBody.create(mediaType, "");

            // 6. 构造POST请求（官方示例的请求方式）
            Request request = new Request.Builder()
                    .url(authUrl)
                    .method("POST", requestBody)
                    .headers(headers)
                    .build();

            // 7. 发送请求并获取响应（官方示例的响应处理逻辑）
            Response response = httpClient.newCall(request).execute();
            String responseBody = response.body().string();
            log.info("百度鉴权接口响应：{}", responseBody);

            // 8. 校验响应状态码（官方未明说，但需确保200 OK）
            if (!response.isSuccessful()) {
                throw new Exception("鉴权请求失败，HTTP状态码：" + response.code() + "，响应：" + responseBody);
            }

            // 9. 解析响应JSON（严格按官方返回格式处理）
            JSONObject jsonObject = new JSONObject(responseBody);
            // 若存在error字段，说明鉴权失败（如AK/SK错误）
            if (jsonObject.has("error")) {
                String errorMsg = jsonObject.getString("error_description");
                log.error("百度鉴权失败：{}", errorMsg);
                throw new Exception("百度鉴权失败：" + errorMsg);
            }

            // 10. 提取token和过期时间，缓存（有效期减10分钟，避免过期）
            String newToken = jsonObject.getString("access_token");
            long expiresIn = jsonObject.getLong("expires_in") * 1000; // 官方返回的是秒，转毫秒
            long newExpireTime = currentTime + expiresIn - 600000; // 提前10分钟（600000毫秒）刷新

            // 更新原子类（线程安全）
            accessToken.set(newToken);
            expireTime.set(newExpireTime);

            log.info("成功获取新的access_token，有效期至：{}", new Date(newExpireTime));
            return newToken;
        }
    }

    // ------------------- 辅助方法（可选，用于测试） -------------------
    /**
     * 手动刷新access_token（用于测试或紧急场景）
     */
    public void refreshAccessToken() throws IOException, JSONException, Exception {
        log.info("手动触发access_token刷新");
        // 清空缓存，下次调用getValidAccessToken会重新获取
        accessToken.set(null);
        expireTime.set(0);
        getValidAccessToken();
    }
}